Options valuation¶

In [1]:
import yfinance as yf 
import numpy as np
import pandas as pd
In [2]:
data = yf.download("GOOG", start="2023-04-17")["Close"]
[*********************100%***********************]  1 of 1 completed
In [3]:
data.head(10)
Out[3]:
Date
2023-04-17    106.419998
2023-04-18    105.120003
2023-04-19    105.019997
2023-04-20    105.900002
2023-04-21    105.910004
2023-04-24    106.779999
2023-04-25    104.610001
2023-04-26    104.449997
2023-04-27    108.370003
2023-04-28    108.220001
Name: Close, dtype: float64
In [4]:
data.tail(10)
Out[4]:
Date
2023-05-15    116.959999
2023-05-16    120.089996
2023-05-17    121.480003
2023-05-18    123.519997
2023-05-19    123.250000
2023-05-22    125.870003
2023-05-23    123.290001
2023-05-24    121.639999
2023-05-25    124.349998
2023-05-26    125.430000
Name: Close, dtype: float64
In [5]:
log_returns = np.log(data/data.shift(1))
log_returns.dropna(inplace=True)
In [6]:
log_returns.head(10)
Out[6]:
Date
2023-04-18   -0.012291
2023-04-19   -0.000952
2023-04-20    0.008344
2023-04-21    0.000094
2023-04-24    0.008181
2023-04-25   -0.020531
2023-04-26   -0.001531
2023-04-27    0.036843
2023-04-28   -0.001385
2023-05-01   -0.004724
Name: Close, dtype: float64
In [12]:
Volatility  = log_returns.std() * sqrt(252)
Volatility
Out[12]:
0.26837635271837934
In [13]:
t = 29/252
In [14]:
# Instantiate BSMCallOption with the required data
S0 = data.tail(1)  # initial stock/index level
K = 130  # strike price
T = (t) # maturity (in year fractions)
r = 0.0511129 # risk-free short rate
sigma = (Volatility) # volatility factor in diffusion term
In [16]:
from math import log, sqrt, exp
from scipy.stats import norm

class BSMCallOption:
    def __init__(self, S0, K, T, r, sigma):
        self.S0 = float(S0)
        self.K = K
        self.T = T
        self.r = r
        self.sigma = sigma

    def value(self):
        d1 = (log(self.S0 / self.K) + (self.r + 0.5 * self.sigma ** 2) * self.T) / (self.sigma * sqrt(self.T))
        d2 = d1 - self.sigma * sqrt(self.T)
        value = self.S0 * norm.cdf(d1) - self.K * exp(-self.r * self.T) * norm.cdf(d2)
        return value

    def vega(self):
        d1 = (log(self.S0 / self.K) + (self.r + 0.5 * self.sigma ** 2) * self.T) / (self.sigma * sqrt(self.T))
        vega = self.S0 * norm.pdf(d1) * sqrt(self.T)
        return vega

    def imp_vol(self, C0, sigma_est=0.2, it=100):
        option = BSMCallOption(self.S0, self.K, self.T, self.r, sigma_est)
        for i in range(it):
            option.sigma -= (option.value() - C0) / option.vega()
        return option.sigma
In [17]:
option = BSMCallOption(S0, K, T, r, sigma)

# Calculate the option value
value = option.value()
print("Option Value:", value)

# Calculate the vega
vega = option.vega()
print("Vega:", vega)

# Estimate implied volatility
C0 = value  # observed option price
implied_volatility = option.imp_vol(C0)
print("Implied Volatility:", implied_volatility)
Option Value: 2.9664470762478103
Vega: 16.308930963559423
Implied Volatility: 0.26837635271837906
In [18]:
maturities = np.linspace(0.05, 2.0, 20) 
strikes = np.linspace(80, 120, 20)
K, T = np.meshgrid(strikes, maturities) 
C = np.zeros_like(K)
V = np.zeros_like(C)
for t in enumerate(maturities):
    for k in enumerate(strikes): 
        option.T = t[1]
        option.K = k[1]
        C[t[0], k[0]] = option.value()
        V[t[0], k[0]] = option.vega()
In [19]:
import plotly.graph_objects as go

# Create a 3D surface plot
fig = go.Figure(data=[go.Surface(x=T, y=K, z=C)])

# Set axis labels and title
fig.update_layout(title='Option', autosize=True,
                  width=900, height=900,
                  margin=dict(l=65, r=50, b=65, t=90))

fig.show()